// *********************************************
// * Raamatukogu  *
// *********************************************
// storyloader.lua - Loads serverside story functionality

module( "story", package.seeall )

local Stories = {}

local BaseStory = {}

BaseStory.InternalName = "base"
BaseStory.PrintName = "Base Story"
BaseStory.Author = (GM or GAMEMODE).Author
BaseStory.Version = "0.00"
BaseStory.Description = "The base story."
BaseStory.Playable = false
BaseStory.Map = { "camerathing" }

BaseStory.PlayerModel = "player/kleiner.mdl"
BaseStory.PlayerWalkSpeed = 100
BaseStory.PlayerRunSpeed = 200
BaseStory.PlayerCrouchSpeed = 10
BaseStory.PlayerCanJump = false
BaseStory.PlayerCanUseFlashlight = false
BaseStory.FirstPerson = false

function BaseStory:LoadChapter( id, ply )
	Msg( "Loading chapter '" .. id .. "'...\n" )
	local path = string.Replace( GAMEMODE.Folder, "gamemodes/", "" ) .. "/gamemode/stories/" .. self.FolderName .. "/"
	_G.STORY = self
	include( path .. "chapter" .. id .. ".lua" )
	STORY = nil
	if (SERVER) then
		umsg.Start( "_loadchapter", ply )
		umsg.String( self.InternalName )
		umsg.Short( id )
		umsg.End()
	end
end

function BaseStory:GetChapterID()
	local curmap = game.GetMap()
	for i=1, #self.Map do
		if (self.Map[i] == curmap) then return i end
	end
end

function BaseStory:GetChapterName()
	return self.Chapters[ self:GetChapterID() ]
end

if (SERVER) then

	function BaseStory:Begin( ply )
		umsg.Start( "_storybegin", ply )
		umsg.String( self.InternalName )
		umsg.End()
	end

	function BaseStory:PlayerSpawn( ply )
		if (self.PlayerCanJump) then
			ply:SetJumpPower( 150 )
		else
			ply:SetJumpPower( 0 )
		end
		ply:SetSpeed( self.PlayerWalkSpeed, self.PlayerRunSpeed, self.PlayerCrouchSpeed)
		ply:CrosshairDisable()
	end
	
	function BaseStory:PlayerLoadout( ply )
	
	end
	
	function BaseStory:PlayerSetModel( ply )
		ply:SetModel( "models/" .. self.PlayerModel )
	end
	
	function BaseStory:PlayerSelectSpawn( ply )
		return GAMEMODE.BaseClass.PlayerSelectSpawn( GAMEMODE, ply )
	end
	
	function BaseStory:PlayerSwitchFlashlight( ply, on )
		if (self.PlayerCanUseFlashlight) then
			return true
		else
			return not on
		end
	end
	
	function BaseStory:HandleEvent( ply, event )
		ply:ChatToSelf( "Event: " .. event )
	end
	
	function BaseStory:Manifest( ply, tbl )
		tbl.Position = ply:GetPos()
		tbl.Angle = ply:GetAimVector():Angle()
		if (!self.FirstPerson) then
			tbl.Camera = ply.ActiveCamera:GetName()
		end
	end
	
	function BaseStory:Restore( ply, tbl )
		ply:SetPos( tbl.Position )
		ply:SetEyeAngles( tbl.Angle )
		if (!self.FirstPerson) then
			ply:SetCamera( tbl.Camera )
		end
	end
	
	function BaseStory:SpawnItem( spawn, class )
		local ent = ents.FindByName( spawn )[1]
		if (!ValidEntity( ent )) then
			ErrorNoHalt( "Failed to locate spawnpoint by name '" .. spawn .. "'!\n" )
			return
		end
		return gstore.CreateItem( class, ent:GetPos() + Vector( 0, 0, 16 ) )
	end
	
	function BaseStory:SpawnNPC( spawn, class, flags )
		local SpawnPointEnt = ents.FindByName( spawn )[1]
		if (ValidEntity( SpawnPointEnt )) then
			local NPC = ents.Create( class )
			NPC:SetPos( SpawnPointEnt:GetPos() )
			if (flags != false) then NPC:SetKeyValue( "spawnflags", flags or 512 )  end
			NPC:Spawn()
			return NPC
		else
			ErrorNoHalt( "Failed to locate spawnpoint by name '" .. spawn .. "'!\n" )
		end
	end
	
	function BaseStory:Finish( ply )
		game.CleanUpMap()
		ply.CameraBrushes = {}
		ply.HandledList = {}
		ply.ActiveStory = nil
		umsg.Start( "the_end", ply )
		umsg.End()
		ply:Freeze( false )
		ply:Blind( false )
		ply:DefenceMode( false )
		ply:KillSilent()
		ply:Spawn()
	end


end

if (CLIENT) then

	function BaseStory:Begin()
		if (!self.FirstPerson) then
			gui.EnableScreenClicker( true )
		end
		LocalPlayer().StoryBegun = true
	end
	
	function BaseStory:Think()
		
	end
	
	function BaseStory:DrawHUD()
	
	end
	
end


function Print( str )
	Msg( "[STORY] " .. str .. "\n" )
end

function Register( tbl )
	local o = {}
	table.Merge( o, tbl )
	setmetatable( o, { __index = BaseStory } )
	local name = tbl.InternalName
	o.BaseClass = BaseStory
	Stories[ name ] = o
	Print( "Registered '" .. name .. "' with " .. #tbl.Chapters .. " chapters!" )
end

function Load( name )
	_G.STORY = {}
	local path = "stories/" .. name .. "/"
	STORY.FolderName = name
	STORY.Folder = path
	include( path .. "settings.lua" )
	include( path .. "items.lua" )
	include( path .. "npcs.lua" )
	if (SERVER) then
		AddCSLuaFile( path .. "settings.lua" )
		AddCSLuaFile( path .. "items.lua" )
		AddCSLuaFile( path .. "npcs.lua" )
		for i=1, #STORY.Chapters do
			AddCSLuaFile( path .. "chapter" .. i .. ".lua" )
		end
	end
	Register( STORY )
	STORY = nil
end

function LoadAll()
	local path = string.Replace( GM.Folder, "gamemodes/", "" ) .. "/gamemode/stories/"
	for _, v in pairs( file.FindInLua( path .. "*" ) ) do
		if ((v != ".") && (v != "..") && (v != ".svn")) then
			local b, err = pcall( Load, v )
			if (!b) then ErrorNoHalt( "[STORY] Failed to load story! (" .. v .. ", " .. err .. ")\n" ) end
		end
	end
end

function Get( name )
	return Stories[ name ]
end

function GetAll()
	return Stories
end

function GetByMap( mapname )
	local temp = {}
	mapname = mapname or game.GetMap()
	for name, story in pairs( Stories ) do
		if (table.HasValue( story.Map, mapname )) then temp[ name ] = story end
	end
	return temp
end

function GetSaveFilename( ply, story )
	local MPFilename = story.InternalName .."/" .. string.Replace( ply:SteamID(), ":", "!" ) .. ".txt"
	local SPFilename = story.InternalName .."/" .. ReplaceInvalidText( ply:GetName() ) .. ".txt"
	return (SinglePlayer() && SPFilename) or MPFilename
end

function SaveState( ply, story )
	local filename = GetSaveFilename( ply, story )
	local o = {}
	story:Manifest( ply, o )
	local b, res = pcall( glon.encode, o )
	if (!b) then return ErrorNoHalt( "Failed to encode story data! (" .. res .. ")\n" ) end
	file.Write( filename, res )
end

function LoadState( ply, story )
	local filename = GetSaveFilename( ply, story )
	if (!file.Exists( filename )) then return ErrorNoHalt( "Could not locate save game! (" .. res .. ")\n" ) end
	local b, res = pcall( glon.decode, file.Read( filename ) )
	if (!b) then return ErrorNoHalt( "Failed to decode story data! (" .. res .. ")\n" ) end
	story:Restore( ply, res )
end

function LockDoor( name )
	local ent = ents.FindByName( name )[1]
	if (!ValidEntity( ent )) then
		ErrorNoHalt( "Unable to locate entity by name '" .. name .. "'!\n" )
		return
	end
	ent:Fire( "Lock" )
	ent.Locked = true
end

function UnlockDoor( name )
	local ent = ents.FindByName( name )[1]
	if (!ValidEntity( ent )) then
		ErrorNoHalt( "Unable to locate entity by name '" .. name .. "'!\n" )
		return
	end
	ent:Fire( "Unlock" )
	ent.Locked = false
end

if (CLIENT) then

	local function umStoryBegin( um )
		local id = um:ReadString()
		local s = Get( id )
		if (!s) then return end
		s:Begin( LocalPlayer() )
	end
	usermessage.Hook( "_storybegin", umStoryBegin )
	
	local function umLoadChapter( um )
		local id = um:ReadString()
		local s = Get( id )
		if (!s) then return end
		local ch = um:ReadShort()
		s:LoadChapter( ch, LocalPlayer() )
	end
	usermessage.Hook( "_loadchapter", umLoadChapter )
	
end